home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-01
/
mc220.zip
/
MTERM.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-02-24
|
17KB
|
676 lines
/*
* MICRO-Terminal:
*
* This is a very simple communications program, which provides
* a subset ANSI (VT100) terminal emulation, and basic XMODEM
* (with checksum) file transfer.
*
* If HOTKEYS are specified on the command line, MTERM will install
* itself as a TSR (Ram-Resident) program, which can be invoked at
* any time by pressing the HOTKEYS. Available HOTKEYS are:
* L - Left SHIFT
* R - Right SHIFT
* A - ALT
* C - CONTROL
* S - SysRq (Caution: some systems may not like this one)
*
* EG: mterm LR (Install with LEFT+RIGHT SHIFT for hotkeys)
*
* This demonstrates the use of the MICRO-C video interface
* and communications library functions for the IBM/PC, as well
* as the "SAVE_VIDEO", "RESTORE_VIDEO" and "TSR" functions.
*
* Copyright 1990,1992 Dave Dunfield
* All rights reserved.
*/
#include \mc\stdio.h /* Standard I/O definitions */
#include \mc\comm.h /* Comm I/O definitions */
#include \mc\video.h /* Video I/O definitions */
#include \mc\file.h /* File I/O definitions */
#include \mc\tsr.h /* Tsr function definitions */
/* Screen output positions */
#define SETROW 3 /* Screen row for settings display */
#define MSGROW 20 /* Screen row for messages */
#define MENROW 7 /* Screen row for menu items */
#define MAICOL 0 /* Screen column for main menu */
#define SUBCOL 20 /* Screen column for sub menu */
#define FILCOL 5 /* Screen column for file prompt */
#define FILSIZ 50 /* Maximum size of file name */
/* XMODEM parameters */
#define BLOCK_SIZE 128 /* size of transmit blocks */
#define RETRYS 10 /* maximum number of retrys */
#define SOH_TIMEOUT 5 /* How long to wait for start of packet */
#define RX_TIMEOUT 2 /* How long in wait for chars in packet */
#define ACK_TIMEOUT 5 /* How long to wait for acknowlege */
/* Line control codes */
#define SOH 0x01 /* start of header */
#define ACK 0x06 /* Acknowledge */
#define NAK 0x15 /* Negative acknowledge */
#define CAN 0x18 /* Cancel */
#define EOT 0x04 /* end of text */
/* Menu text tables (Used by 'vmenu') */
char *main_menu[] = {
"Terminal Emulation",
"XMODEM Download",
"XMODEM Upload",
"Serial port config",
"Exit to DOS",
0 };
char *setup_menu[] = {
"Comm port",
"Baudrate",
"Data bits",
"Parity",
"Stop bits",
"Xon/Xoff",
0 };
/* Uart configuration static data tables */
unsigned baudvalue[] =
{ _110, _300, _1200, _2400, _4800, _9600, _19200, _38400 };
char *baudtext[] =
{ "110", "300", "1200", "2400", "4800", "9600", "19200", "38400", 0 };
char *databits[] = { "Five", "Six", "Seven", "Eight", 0 };
char *parity[] = { "Odd", "Even", "Mark", "Space", "None", 0 };
char *onetwo[] = { "One", "Two", 0 };
char *flowctrl[] = { "Disabled", "Enabled", 0 };
/* Communications configuration parameters */
int comm = 0, baud = 5, data = 3, par = 4, stop = 0, flow = 1;
/* Misc global variables */
setup_selection = 0, transfer_selection = 0;
char dfile[FILSIZ+1] = "", ufile[FILSIZ+1] = "";
/* Saved video screens, attributes & cursor position */
char sav_buffer[(25*80)*2], sav_attr;
int sav_xy;
char video_save_area[SCR_BUF];
/*
* Main terminal program menu
*/
tty_main()
{
int i;
i = 0; /* Default to top of menu */
save_video(video_save_area);
redraw:
draw_title();
vdraw_box(0, SETROW, 79, 2);
show_settings();
for(;;) {
message("Select function and press ENTER");
if(vmenu(MAICOL, MENROW, main_menu, 0, &i))
continue;
switch(i) {
case 0 : /* Terminal Emulation */
if(!open_comm(flow))
break;
vcursor_line();
restore_screen();
ansi_term();
save_screen();
vcursor_off();
goto redraw;
case 1 : /* Download a file */
if(open_comm(0))
download(dfile);
break;
case 2 : /* Upload a file */
if(open_comm(0))
upload(dfile);
break;
case 3 : /* Setup serial port */
setup();
break;
case 4 : /* Exit to DOS */
Cclose();
restore_video(video_save_area);
return; } }
}
/*
* Open a file for read or write (with overwrite prompt)
*/
HANDLE openf(fname, rw)
char *fname, rw;
{
char c, omsg[80], *mode;
HANDLE fh;
mode = "read";
fh = open(fname, F_READ); /* First try and read the file */
if(rw) { /* If writing the file */
mode = "write";
if(fh) {
close(fh);
sprintf(omsg, "Overwrite existing %s (Y/N) ?", fname);
message(omsg);
do {
c = toupper(vgetc());
if((c == 0x1B) || (c == 'N'))
return 0; }
while(c != 'Y'); }
fh = open(fname, F_WRITE); }
if(!fh) {
sprintf(omsg,"Cannot %s %s (Press ENTER)", mode, fname);
message(omsg);
while(vgetc() != '\n'); }
return fh;
}
/*
* Open comm port with correct settings
*/
open_comm(flow)
char flow;
{
int mode;
/* Calculate the communications parameter value */
mode = ((par << 4) & 0x30) | /* parity type */
(data & 0x03) | /* # data bits */
((stop << 2) & 0x04) | /* # stop bits */
((par < 4) << 3); /* parity enable */
/* Open the communications port */
if(Copen(comm+1, baudvalue[baud], mode, SET_DTR|SET_RTS|OUTPUT_2)) {
message("Cannot open COM port (Press ENTER)");
while(vgetc() != '\n');
return 0; }
/* Remove transparency if XON/XOFF flow control */
disable();
Cflags = (flow) ? Cflags & ~TRANSPARENT : Cflags | TRANSPARENT;
enable();
return -1;
}
/*
* Draw the title header
*/
draw_title()
{
vopen();
V_ATTR = REVERSE;
vdraw_box(0, 0, 79, 2);
vgotoxy(1, 1);
vputf("", 26);
vputf("MICRO-Terminal Version 1.2", 52);
V_ATTR = NORMAL;
vcursor_off();
}
/*
* Draw the file transfer information box
*/
info_box(mode, filename)
char *mode, *filename;
{
vdraw_box(SUBCOL, MENROW+1, 50, 8);
vgotoxy(SUBCOL+2, MENROW+3);
vprintf("%-19s: %s", mode, filename);
vgotoxy(SUBCOL+2, MENROW+5);
vputs("Blocks transferred : 0");
vgotoxy(SUBCOL+2, MENROW+7);
vputs("Transfer status : ");
message("File transfer in progress (ESCAPE to abort)");
}
/*
* Update the transfer status field
*/
transfer_status(text)
char *text
{
vgotoxy(SUBCOL+23, MENROW+7);
vputf(text,10);
}
/*
* Show the current COM port settings
*/
show_settings()
{
vgotoxy(18, SETROW+1);
vprintf("COM%u: %5s,%2d,%5s,%2d Xon/Xoff %-8s",
comm+1, baudtext[baud], data+5, parity[par], stop+1, flowctrl[flow]);
}
/*
* Display a message
*/
message(ptr)
char *ptr;
{
vgotoxy(0, MSGROW);
vcleos();
vmessage(38 - strlen(ptr)/2, MSGROW, ptr);
}
/*
* Save the MICRO-TERMINAL video screen.
*/
save_screen()
{
sav_xy = V_XY;
sav_attr = V_ATTR;
copy_seg(get_ds(), sav_buffer, V_BASE, 0, (25*80)*2);
}
/*
* Restore the MICRO-TERMINAL video screen
*/
restore_screen()
{
copy_seg(V_BASE, 0, get_ds(), sav_buffer, (25*80)*2);
V_ATTR = sav_attr;
V_XY = sav_xy;
vupdatexy();
}
/*
* Comm port setup menu handler
*/
setup()
{
message("Select setting (ESCAPE to cancel)");
for(;;) {
show_settings();
if(vmenu(SUBCOL, MENROW+1, setup_menu, 0, &setup_selection))
return;
switch(setup_selection) {
case 0 : /* Comm port */
vmenu(SUBCOL+11,MENROW+2,onetwo,-1,&comm);
break;
case 1 : /* baudrate */
vmenu(SUBCOL+11,MENROW+2,baudtext,-1,&baud);
break;
case 2 : /* Data bits */
vmenu(SUBCOL+11,MENROW+2,databits,-1,&data);
break;
case 3 : /* Parity */
vmenu(SUBCOL+11,MENROW+2,parity,-1,&par);
break;
case 4 : /* Stop bits */
vmenu(SUBCOL+11,MENROW+2,onetwo,-1,&stop);
break;
case 5 : /* Flow control */
vmenu(SUBCOL+11,MENROW+2,flowctrl,-1,&flow); } }
}
/*
* ANSI (VT100) Function key translation table
*/
char *ansi_keys[] = {
"\x1B[A", "\x1B[B", "\x1B[D", "\x1B[C", /* Arrow keys */
"\x1BOR", "\x1BOS", "\x1BOP", "\x1BOQ", /* PgUp, Pgdn, Home, End */
"\x1BOM", "\x1BOm", "\x1BOp", /* Keypad '+','-' Insert */
"\x7F", "\x08", /* Delete & Backspace */
"\x1BOq", "\x1BOr", "\x1BOs", "\x1BOt", /* F1, F2, F3 & F4 */
"\x1BOu", "\x1BOv", "\x1BOw", "\x1BOx", /* F5, F6, F7 & F8 */
"\x1BOy", "\x1BOz", /* F9 & F10 */
"\x1BOl", "\x1BOn", 0, 0 }; /* Control: Pgup, Pgdn, Home, End */
/*
* Terminal mode using ANSI (VT100) emulation
*/
ansi_term()
{
char c, xy_flag, *ptr;
unsigned x, y, state, value, parm, parms[5];
xy_flag = -1; /* Force initial cursor update */
state = 0; /* Not receiving a control sequence */
for(;;) {
/* Process any input from the comm port */
if((c = Ctestc()) != -1) {
xy_flag = -1;
if(c == 0x1B) { /* Begin escape sequence */
state = 1;
parms[0] = parms[1] = value = parm = 0; }
else switch(state) {
case 1 : /* Escape already received */
if(c == '[') {
state = 2;
break; }
state = 0;
case 0 : /* No special processing */
vputc(c);
break;
case 2 : /* Waiting for numeric parms */
if(isdigit(c)) {
value = (value * 10) + (c - '0');
break; }
parms[parm++] = value; /* More to come */
if(c == ';') {
value = 0;
break; }
state = 0;
switch(c) {
case 'H' : /* Cursor position (1) */
case 'f' : /* Cursor position (2) */
if(y = parms[0])
--y;
if(x = parms[1])
--x;
vgotoxy(x, y);
break;
case 'J' : /* Erase in display */
x = V_XY;
value ? vclscr() : vcleos();
V_XY = x;
break;
case 'K' : /* Erase in line */
x = V_XY;
if(value)
V_XY &= 0xff00;
vcleol();
V_XY = x;
break;
case 'm' : /* Select attributes */
x = 0;
do {
V_ATTR = (y = parms[x]) ? (y == 4) ?
UNDERLINE : REVERSE : NORMAL; }
while(++x < parm); } } }
else if(xy_flag) { /* Cursor has moved */
vupdatexy();
xy_flag = 0; }
/* Process any input from the keyboard */
if(c = vtstc()) {
if(c & 0x80) { /* Special function key */
if(!(ptr = ansi_keys[c & 0x7f]))
return;
while(*ptr)
Cputc(*ptr++); }
else
Cputc((c == '\n') ? '\r' : c); } }
}
/*
* Receive a file in XMODEM protocol
*/
download()
{
char rbuffer[BLOCK_SIZE], *error_text, *old_error, ochr;
int r, rx_block_num, error_count;
HANDLE fh;
if(vgets(FILCOL,MSGROW,"Write to file? ",dfile,FILSIZ) || !*dfile)
return;
if(!(fh = openf(dfile, -1)))
return;
info_box("Download to file", dfile);
error_text = old_error = rx_block_num = 1;
error_count = RETRYS;
do {
if(vtstc() == 0x1b) { /* Console escape */
ochr = CAN;
error_text = "CANCELED";
error_count = 0; }
else if((r = get_record(rbuffer)) == (rx_block_num & 255)) {
error_count = RETRYS;
write(rbuffer, BLOCK_SIZE, fh);
vgotoxy(SUBCOL+23, MENROW+5);
vprintf("%u", rx_block_num++);
error_text = "RX PACKET";
ochr = ACK; }
else {
switch(r) {
case -1 : /* Timeout */
error_text = "TIMEOUT";
ochr = NAK;
break;
case -2 : /* Bad block */
error_text = "BAD BLOCK#";
while(Cgett(RX_TIMEOUT) != -1);
ochr = NAK;
break;
case -3 : /* Bad checksum */
error_text = "BAD CHKSUM";
ochr = NAK;
break;
case -4 : /* End of file */
error_text = "DONE";
ochr = ACK;
break;
case -5 : /* Cancel */
error_text = "ABORTED";
ochr = ACK;
break;
default: /* Block out of sequence */
error_text = "WRONG BLK";
ochr = NAK; }
--error_count; }
Cputc(ochr);
/* Update status message */
if(error_text != old_error)
transfer_status(old_error = error_text); }
while((r > -3) && error_count);
message("Download ended (Press ENTER)");
close(fh);
while(vgetc() != '\n');
vclear_box(SUBCOL, MENROW+1, 50, 8);
}
/*
* Read a record in the XMODEM protocol, return the block number
* (0-255) if successful, or one of the following return codes:
* -1 = Timeout
* -2 = Bad block number
* -3 = Bad block checksum
* -4 = End of file
* -5 = Canceled by remote
*/
get_record(rbuffer)
char rbuffer[];
{
int c, i, block_num, check_sum;
check_sum = 0;
i = -2;
switch(Cgett(SOH_TIMEOUT)) {
case SOH : /* Receive packet */
for(;;) {
if((c = Cgett(RX_TIMEOUT)) == -1) /* receive timeout */
break;
if(i == -2) /* first block number */
block_num = c;
else if(i == -1) { /* second block number */
if((255 & ~c) != block_num)
return -2; }
else if(i == BLOCK_SIZE) /* checksum at end */
return (check_sum & 0xff) == c ? block_num : -3;
else /* data character */
check_sum += (rbuffer[i] = c);
++i; }
case -1 : /* timeout on waiting for packet */
return -1;
case EOT : /* end of file encountered */
return -4;
case CAN : /* cancel protocol */
return -5; }
}
/*
* Transmit a file in XMODEM protocol
*/
upload()
{
int i, c, tx_block_num, error_count;
char buffer[BLOCK_SIZE], *error_text, *old_error;
HANDLE fh;
if(vgets(FILCOL,MSGROW,"Read from file? ",ufile,FILSIZ) || !*ufile)
return;
if(!(fh = openf(ufile, 0)))
return;
info_box("Upload from file", ufile);
tx_block_num = old_error = error_text = 1;
error_count = RETRYS;
/* Transmit the file data */
while(i = read(buffer, BLOCK_SIZE, fh)) {
while(i < 128)
buffer[i++] = -1;
error_text = "TX PACKET";
while(i = send_record(buffer, tx_block_num)) {
switch(i) {
case -1 :
error_text = "TIMEOUT";
break;
case -3 :
error_text = "RECV NAK";
break;
case -5 :
error_text = "ABORTED"; }
transfer_status(old_error = error_text);
if((i < -3) || !error_count--)
break; }
if(vtstc() == 0x1b) { /* Console escape */
i = -5;
error_text = "CANCELED"; }
if(i) { /* Error exit */
Cputc(CAN);
break; }
error_count = RETRYS;
vgotoxy(SUBCOL+23, MENROW+5);
vprintf("%u", tx_block_num++);
if(error_text != old_error)
transfer_status(old_error = error_text); }
/* Send the end of file indicator */
error_count = RETRYS;
if(!i) for(;;) {
Cputc(EOT);
if((c = Cgett(ACK_TIMEOUT)) == ACK) {
error_text = "DONE";
break; }
if(c == CAN) {
error_text = "ABORTED";
break; }
if(((c == -1) || (c == NAK)) && !error_count--) {
error_text = "TIMEOUT";
break; } }
transfer_status(error_text);
message("Upload ended (Press ENTER)");
close(fh);
while(vgetc() != '\n');
vclear_box(SUBCOL, MENROW+1, 50, 8);
}
/*
* Send an record in XMODEM protocol, return 0 if successful
* Otherwise, return one of the following:
* -1 = Timeout
* -2 = Bad block number (N/A)
* -3 = Bad block (NAK RECEIVED)
* -4 = End of file (N/A)
* -5 = Canceled by remote
*/
send_record(buffer, block_num)
char *buffer;
int block_num;
{
int i, check_sum;
char *ptr;
check_sum = 0;
ptr = buffer;
while(Ctestc() != -1); /* purge any received data */
Cputc(SOH);
Cputc(block_num);
Cputc(~block_num);
for(i=0; i < BLOCK_SIZE; ++i) {
Cputc(*buffer);
check_sum += *buffer++; }
Cputc(check_sum);
for(;;) switch(Cgett(ACK_TIMEOUT)) {
case ACK : /* Packet received ok */
return 0;
case NAK : /* Rejected */
return -3;
case CAN : /* Remote cancel */
return -5;
case -1 : /* Timeout */
return -1; }
}
/*
* Wait for a character from the modem (with timeout)
*/
Cgett(timeout)
unsigned timeout;
{
int h, m, s, old_s;
if((h = Ctestc()) != -1) /* Very fast if characters buffered */
return h;
get_time(&h, &m, &old_s);
do {
do {
if((h = Ctestc()) != -1)
return h;
get_time(&h,&m,&s); }
while(s == old_s);
old_s = s; }
while(--timeout);
return -1;
}
/*
* Main program, either TSR or execute main tty program menu
*/
main(argc, argv)
int argc;
int *argv[];
{
int hot_keys;
char *ptr;
vopen();
vputs("MICRO-Terminal: (Press CTRL-HOME or CTRL-END to exit):\n");
save_screen();
/* If RAM-resident, print startup message & TSR */
if(argc > 1) {
draw_title();
printf("\n\n\nCopyright 1990,1992 Dave Dunfield\nAll rights reserved.\n");
vcursor_line();
hot_keys = 0;
ptr = argv[1];
while(*ptr) switch(toupper(*ptr++)) {
case 'A' : hot_keys |= ALT; break;
case 'C' : hot_keys |= CONTROL; break;
case 'L' : hot_keys |= L_SHIFT; break;
case 'R' : hot_keys |= R_SHIFT; break;
case 'S' : hot_keys |= SYS_REQ; break;
default: abort("\nInvalid HOTKEY"); }
tsr(&tty_main, hot_keys, 2000); }
/* Not RAM-resident, execute the program */
vclscr(); /* Return to blank screen */
tty_main();
}